// Tencent is pleased to support the open source community by making
// 蓝鲸智云 - 监控平台/日志平台 (BlueKing - Monitor/Log) available.
// Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://opensource.org/licenses/MIT
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

package converter

import (
	"encoding/json"

	"github.com/elastic/beats/libbeat/common"
	"go.opentelemetry.io/collector/model/pdata"

	"github.com/TencentBlueKing/bk-collector/define"
	"github.com/TencentBlueKing/bkmonitor-kits/logger"
)

type logsEvent struct {
	define.CommonEvent
}

func (e logsEvent) RecordType() define.RecordType { return define.RecordLogs }

var LogsConverter EventConverter = logsConverter{}

type logsConverter struct{}

func (c logsConverter) ToEvent(dataId int32, data common.MapStr) define.Event {
	return logsEvent{define.NewCommonEvent(dataId, data)}
}

func (c logsConverter) ToDataID(record *define.Record) int32 {
	return record.Token.LogsDataId
}

func (c logsConverter) Convert(record *define.Record) define.Events {
	var events define.Events
	pdLogs, ok := record.Data.(pdata.Logs)
	if !ok {
		return events
	}
	resourceLogs := pdLogs.ResourceLogs()
	if resourceLogs.Len() == 0 {
		return nil
	}
	dataId := c.ToDataID(record)

	for i := 0; i < resourceLogs.Len(); i++ {
		resourceLog := resourceLogs.At(i)
		resources := resourceLog.Resource().Attributes().AsRaw()
		instLogs := resourceLog.InstrumentationLibraryLogs()
		for j := 0; j < instLogs.Len(); j++ {
			instLog := instLogs.At(j).Logs()
			for k := 0; k < instLog.Len(); k++ {
				content, err := c.Extract(record.RequestClient.IP, instLog.At(k), resources)
				if err != nil {
					logger.Warnf("failed to extract content: %v", err)
					continue
				}
				events = append(events, c.ToEvent(dataId, content))
			}
		}
	}

	return events
}

func (c logsConverter) Extract(ip string, pdLog pdata.LogRecord, resources common.MapStr) (common.MapStr, error) {
	m := common.MapStr{
		"time_unix":       pdLog.Timestamp() / 1000,
		"logs_name":       pdLog.Name(),
		"span_id":         pdLog.SpanID().HexString(),
		"trace_id":        pdLog.TraceID().HexString(),
		"attributes":      pdLog.Attributes().AsRaw(),
		"body":            pdLog.Body().AsString(),
		"flags":           pdLog.Flags(),
		"severity_number": pdLog.SeverityNumber(),
		"severity_text":   pdLog.SeverityText(),
		"resource":        resources,
	}
	content, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}

	return common.MapStr{
		"data":   string(content),
		"source": ip,
	}, nil
}
